package ro.flashbite.helpers
{
	import ro.flashbite.errors.ArgumentIncompatibleError;
	import ro.flashbite.errors.DontInstanciateError;

	/**
	 * Static helpers for working with string objects
	 * 
	 * v 1.0
	 * 
	 * @author Adrian Barbu  
	 */	
	public final class StringHelpers
	{
		private static const 	WHITESPACE	:	String = ' \n\t\r';
		
		public function StringHelpers() { throw new DontInstanciateError(); }
		
		/**
		 * Search all the words from string
		 * 
		 * @param string : (String) string to be searched
		 * @return : (Array) an array with all words in string
		 */		
		public static function getRealWords(string:String):Array
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern : RegExp =  /([a-z-A-Z-0-9-'_]+)()/g;
			
			return string.match(pattern);
		}
		
		/**
		 * Counts the number of words in a String
		 * 
		 * @param string : (String) string to be searched
		 * @return : (uint) the number of words in string
		 */		
		public static function getWordCount(string:String):uint
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			return getRealWords(string).length;
		}
		
		/**
		 * Determines if string is only composed of punctuation characters 
		 * 
		 * @param string : (String) string to be used
		 * @param allowSpaces : (Boolean) flag to be set if in search parameters can be used spaces
		 * @return : (Boolean) true if string is only punctuation, otherwise false
		 */		
		public static function isPunctuation(string:String, allowSpaces:Boolean = true):Boolean 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			if (getNumbersFromString(string).length != 0 || getLettersFromString(string).length != 0)
				return false;
			
			if (!allowSpaces)
				return string.split(' ').length == 1;
			
			return true;
		}
		
		/**
		 * Capitalize first letter in string
		 * 
		 * @param string : (String) string to be used
		 * @return : (String) the string with the first letter capitalized
		 */		
		public static function capitalizeFirstLetter(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var i			:int = -1,
				stringLen	:int = string.length;
			
			while (++i < stringLen)
				if (!isPunctuation(string.charAt(i)))
					return replaceAt(string, i, string.charAt(i).toUpperCase());
			
			return string;
		}
		
		/**
		 * Returns all the letter characters from a String
		 * 
		 * @param string : (String) string to be used 
		 * @return : (String) a string representation with only letters from the init string
		 */		
		public static function getLettersFromString(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern : RegExp = /[^A-Z^a-z]/g;
			return string.replace(pattern, '');
		}
		
		/**
		 * Returns all the number characters from a String
		 * 
		 * @param string : (String) string to be used 
		 * @return : (String) a string representation with only numbers from the init string
		 */		
		public static function getNumbersFromString(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern : RegExp = /[^0-9]/g;
			return string.replace(pattern, '');
		}
		
		/**
		 * Replaces a character at a specific index with new characters
		 * 
		 * @param string : (String) string to be used
		 * @param position : (uint) position in string were the replacement will be done
		 * @param replace : (String) string to be put in the place of the character in position given 
		 * @return : (String) string with character replaced
		 */		
		public static function replaceAt(string:String, position:uint, replace:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			if (replace == null) {
				throw new ArgumentIncompatibleError("replace");
			}
			if (position > string.length) {
				throw new ArgumentIncompatibleError("position");
			}
			
			var parts:Array = string.split('');
			parts.splice(position, 1, replace);
			
			return parts.join('');
		}
		
		/**
		 * Adds characters at a specific index
		 * 
		 * @param string : (String) string to be used
		 * @param position : (uint) position in string were the addition will be done
		 * @param addition : (String) string to be added in position
		 * @return : (String) string with character added
		 */		
		public static function addAt(string:String, position:int, addition:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			if (addition == null) {
				throw new ArgumentIncompatibleError("addition");
			}
			if (position > string.length) {
				throw new ArgumentIncompatibleError("position");
			}
			
			var parts : Array = string.split('');
			parts.splice(position, 0, addition);
			
			return parts.join('');
		}
		
		/**
		 * Remove specific texts from string
		 * 
		 * @param string : (String) string to be used
		 * @param texts : (Array) array with strings to be remoced from initial string
		 * @return : (String) the new string with given strings removed
		 */		
		public static function removeTextsFromString(string:String, texts:Array):String
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			if (texts == null) {
				throw new ArgumentIncompatibleError("texts");
			}
			
			var data:String = string;
			
			for each (var text:String in texts) {
				data = data.replace(text, "");
			}
			
			return data;
		}
		
		/**
		 * Strips whitespace from the end of a String
		 * 
		 * @param string : (String) string to be used
		 * @return : (String) string with characters removed
		 */		
		public static function trimRight(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern : RegExp = new RegExp('[' + WHITESPACE + ']+$', '');
			
			return string.replace(pattern, '');
		}
		
		/**
		 * Strips whitespace from the beginning of a String
		 * 
		 * @param string : (String) string to be used
		 * @return : (String) string with characters removed
		 */		
		public static function trimLeft(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern : RegExp = new RegExp('^[' + WHITESPACE + ']+', '');
			
			return string.replace(pattern, '');
		}
		
		/**
		 * Strips whitespace from the beginning and end of a String
		 * 
		 * @param string : (String) string to be used
		 * @return : (String) string with characters removed
		 */		
		public static function trim(string:String):String 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			return trimLeft(trimRight(string));
		}
		
		/**
		 * Determines if string is a valid email address
		 * 
		 * @param string : (String) text to be checked
		 * @return : (Boolean) true if the string is a valid e-mail adress, false otherwise
		 */		
		public static function isEmail(string:String):Boolean 
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			var pattern:RegExp = /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$/i;
			
			return string.match(pattern) != null;
		}
		
		/**
		 * Determines if string is empty ("") or null
		 * 
		 * @param string : (String) text to be checked
		 * @return : (Boolean) true if empty, false otherwise
		 */		
		public static function isEmpty(string:String):Boolean 
		{
			if (string) return false;
			return true;
		}
		
		/**
		 * Determines if string is is whitespace ("     "), empty ("") or null
		 * 
		 * @param string : (String) text to be checked
		 * @return : (Boolean) true if empty, false otherwise
		 */		
		public static function isBlank(string:String):Boolean
		{
			if (string == null) {
				throw new ArgumentIncompatibleError("string");
			}
			
			return isEmpty(trim(string));
		}
		
		/**
		 * Determines if two string are equal ignoring case
		 * 
		 * @param string1 : (String) first text to be checked
		 * @param string2 : (String) second text to be checked
		 * @return : (Boolean) true if the strings are equal, false otherwise
		 */		
		public static function equalsIgnoreCase(string1:String, string2:String):Boolean
		{
			if (string1 == null) {
				throw new ArgumentIncompatibleError("string1");
			}
			if (string2 == null) {
				throw new ArgumentIncompatibleError("string2");
			}
			
			return (string1.toLowerCase() == string2.toLowerCase());
		}
	}
}